/**
 * 监控预约任务执行器
 *
 * 独立进程，从任务队列中取任务并执行预约
 * 实现监控与预约分离
 */

import type { AppointmentManagement } from '@shared/types/'
import { normalizeAppointmentTime } from '@shared/utils/appointment-time'
import { createContextLogger, generateCorrelationId, log, PerformanceLogger } from '../logger/index'
import { LogCategory } from '../logger/types'
import { getAppointmentService } from '../services/appointment-service'
import { getMonitoringBookingTaskService } from '../services/monitoring-booking-task-service'
import { getUnifiedBookingService } from './booking-service'

export interface MonitoringTaskExecutorOptions {
  pollingInterval?: number // 轮询间隔（毫秒），默认2000
  concurrency?: number // 并发数，默认5
  enabled?: boolean // 是否启用，默认true
}

class MonitoringTaskExecutor {
  private running = false
  private logger = createContextLogger({ component: 'MonitoringTaskExecutor', category: LogCategory.MONITOR })
  private pollingInterval: number
  private concurrency: number
  private enabled: boolean
  private processingTasks = new Set<number>() // 正在处理的任务ID
  private abortController: AbortController | null = null
  private cooldownUntilByAppointmentId = new Map<number, number>() // 任务冷却时间（避免立即重试）

  constructor(options: MonitoringTaskExecutorOptions = {}) {
    this.pollingInterval = options.pollingInterval ?? 2000
    this.concurrency = options.concurrency ?? 5
    this.enabled = options.enabled ?? true
  }

  /**
   * 动态调整并发数
   */
  setConcurrency(n: number): void {
    const v = Math.max(1, Math.min(200, Math.floor(Number(n) || 0)))
    const prev = this.concurrency
    this.concurrency = v
    try {
      this.logger.info('监控任务执行器并发已更新', { previous: prev, current: v })
    }
    catch {}
  }

  /**
   * 启动执行器
   */
  async start(): Promise<void> {
    if (this.running)
      return

    if (!this.enabled) {
      this.logger.info('监控任务执行器已禁用')
      return
    }

    this.running = true
    this.abortController = new AbortController()
    this.logger.info('监控任务执行器已启动', {
      pollingInterval: this.pollingInterval,
      concurrency: this.concurrency,
    })

    // 启动轮询循环
    this.pollLoop().catch((error) => {
      this.logger.error('执行器轮询循环异常', { error: error?.message || String(error) })
    })
  }

  /**
   * 停止执行器
   */
  async stop(): Promise<void> {
    if (!this.running)
      return

    this.running = false
    try {
      this.abortController?.abort()
    }
    catch {}

    // 等待正在处理的任务完成
    while (this.processingTasks.size > 0) {
      this.logger.info('等待任务完成', { 剩余: this.processingTasks.size })
      await this.delay(1000)
    }

    this.logger.info('监控任务执行器已停止')
  }

  /**
   * 轮询循环
   */
  private async pollLoop(): Promise<void> {
    let consecutiveErrors = 0
    const maxConsecutiveErrors = 5

    while (this.running) {
      try {
        const pollCorrelationId = generateCorrelationId()
        PerformanceLogger.start('MonitoringTaskExecutor.poll')
        // 检查是否有空闲槽位
        const availableSlots = this.concurrency - this.processingTasks.size
        if (availableSlots > 0) {
          // 获取待处理任务
          const taskService = getMonitoringBookingTaskService()
          // 多取一些，过滤掉处于冷却期的任务
          const pending = await taskService.getPendingTasks(availableSlots * 3)
          const nowTs = Date.now()
          const tasks = pending.filter((t) => {
            const until = this.cooldownUntilByAppointmentId.get(t.appointment_id) || 0
            return until <= nowTs
          }).slice(0, availableSlots)

          // 重置连续错误计数
          consecutiveErrors = 0

          // 并发处理任务
          for (const task of tasks) {
            if (!this.running)
              break

            if (this.processingTasks.size >= this.concurrency)
              break

            // 标记为处理中
            try {
              await taskService.markAsProcessing(task.id)
              this.processingTasks.add(task.id)

              // 异步执行任务（不等待完成）
              this.executeTask(task, pollCorrelationId).finally(() => {
                this.processingTasks.delete(task.id)
              })
            }
            catch (markError: any) {
              // 标记失败可能是表不存在，跳过这个任务
              this.logger.debug('标记任务为处理中失败，跳过', {
                task_id: task.id,
                error: markError?.message || String(markError),
                correlationId: pollCorrelationId,
              })
            }
          }

          // 性能记录
          const duration = PerformanceLogger.end('MonitoringTaskExecutor.poll')
          log.performance('MonitoringTaskExecutor.poll', duration, {
            component: 'MonitoringTaskExecutor',
            category: LogCategory.MONITOR,
            correlationId: pollCorrelationId,
            availableSlots,
            fetched: Array.isArray(tasks) ? tasks.length : 0,
            processingCount: this.processingTasks.size,
            concurrency: this.concurrency,
          })
        }
      }
      catch (error: any) {
        consecutiveErrors++
        const errorMsg = error?.message || String(error)

        // 如果是表不存在的错误，使用debug级别
        if (errorMsg.includes('does not exist') || errorMsg.includes('no such table') || errorMsg.includes('Unknown model')) {
          // 只在连续错误达到阈值时记录一次，避免日志刷屏
          if (consecutiveErrors === maxConsecutiveErrors) {
            this.logger.debug('任务表可能尚未创建，等待中...', {
              error: errorMsg,
              correlationId: generateCorrelationId(),
            })
          }
        }
        else {
          // 其他错误才记录error级别
          this.logger.error('轮询任务失败', {
            error: errorMsg,
            consecutiveErrors,
            correlationId: generateCorrelationId(),
          })
        }
      }

      // 等待轮询间隔
      await this.delayCancellable(this.pollingInterval)
    }
  }

  /**
   * 执行单个任务
   */
  private async executeTask(task: any, correlationId?: string): Promise<void> {
    const taskService = getMonitoringBookingTaskService()
    const appointmentService = getAppointmentService()
    const bookingService = getUnifiedBookingService()

    try {
      this.logger.info('开始执行预约任务', {
        task_id: task.id,
        appointment_id: task.appointment_id,
        available_date: task.available_date,
        correlationId,
      })

      // 获取预约信息
      const appointment = await appointmentService.getAppointment(task.appointment_id)
      if (!appointment) {
        await taskService.markAsFailed(task.id, '预约记录不存在', false)
        return
      }

      // 检查预约状态（如果已经预约成功，取消任务）
      if (appointment.appointment_status === 'booked') {
        await taskService.markAsCompleted(task.id)
        this.logger.info('预约已成功，取消任务', {
          task_id: task.id,
          appointment_id: task.appointment_id,
          correlationId,
        })
        return
      }

      // 解析预约选项
      const bookingOptions = task.booking_options
        ? JSON.parse(task.booking_options)
        : {}

      // 设置目标日期（如果指定了preferredDate）
      if (bookingOptions.preferredDate) {
        bookingOptions.preferredDates = [bookingOptions.preferredDate]
      }

      // 执行预约
      const result = await bookingService.bookWith(
        task.booking_mode as 'playwright' | 'request',
        appointment as AppointmentManagement,
        bookingOptions,
      )

      // 处理预约结果
      if (result.success) {
        // 更新预约记录
        await appointmentService.updateAppointment(task.appointment_id, {
          appointment_status: 'booked',
          reference_number: result.referenceNumber,
          appointment_date: result.selectedDate,
          appointment_time: normalizeAppointmentTime(result.selectedTimeslot),
        } as any)

        // 标记任务为完成
        await taskService.markAsCompleted(task.id)

        this.logger.info('预约任务执行成功', {
          task_id: task.id,
          appointment_id: task.appointment_id,
          reference_number: result.referenceNumber,
          appointment_date: result.selectedDate,
          correlationId,
        })
      }
      else {
        // 判断是否应该重试
        const shouldRetry = this.shouldRetry(result.error || '')
        await taskService.markAsFailed(task.id, result.error || '预约失败', shouldRetry)

        this.logger.warn('预约任务执行失败', {
          task_id: task.id,
          appointment_id: task.appointment_id,
          error: result.error,
          shouldRetry,
          correlationId,
        })

        // 根据错误类型施加指数回退冷却（不占用并发槽位）
        if (shouldRetry) {
          const nextRetry = Number(task.retry_count || 0) + 1
          const delayMs = this.computeBackoffMs(result.error || '', nextRetry)
          if (delayMs > 0) {
            const until = Date.now() + delayMs
            this.cooldownUntilByAppointmentId.set(task.appointment_id, until)
            try { this.logger.info('monitor.retry_backoff', { task_id: task.id, appointment_id: task.appointment_id, retry_count: nextRetry, delayMs }) }
            catch {}
          }
        }
      }
    }
    catch (error: any) {
      const errorMessage = error?.message || String(error)
      const shouldRetry = this.shouldRetry(errorMessage)
      await taskService.markAsFailed(task.id, errorMessage, shouldRetry)

      this.logger.error('执行预约任务异常', {
        task_id: task.id,
        error: errorMessage,
        shouldRetry,
        correlationId,
      })

      if (shouldRetry) {
        const nextRetry = Number(task.retry_count || 0) + 1
        const delayMs = this.computeBackoffMs(errorMessage, nextRetry)
        if (delayMs > 0) {
          const until = Date.now() + delayMs
          this.cooldownUntilByAppointmentId.set(task.appointment_id, until)
          try { this.logger.info('monitor.retry_backoff', { task_id: task.id, appointment_id: task.appointment_id, retry_count: nextRetry, delayMs }) }
          catch {}
        }
      }
    }
  }

  /**
   * 判断是否应该重试
   */
  private shouldRetry(error: string): boolean {
    // 以下错误不应该重试
    const noRetryErrors = [
      '预约记录不存在',
      'already booked',
      '已预约',
      'duplicate',
    ]

    const errorLower = error.toLowerCase()
    return !noRetryErrors.some(noRetry => errorLower.includes(noRetry.toLowerCase()))
  }

  /**
   * 根据错误类型计算指数回退时间（毫秒）
   */
  private computeBackoffMs(error: string, retryCount: number): number {
    const e = String(error || '').toLowerCase()
    const isDupLogin = /512-e-0044|duplicate_login|重[複覆]登入|系统而中断|系統而中斷/.test(e)
    const isTimeout = /connect timeout|etimedout|timeout/.test(e)
    const isAborted = /this operation was aborted|aborted/.test(e)

    let base = 0
    let max = 0
    if (isDupLogin) { base = 5000; max = 30000 }
    else if (isTimeout || isAborted) { base = 700; max = 10000 }
    else { return 0 }

    const exp = Math.max(1, Math.min(5, retryCount)) - 1
    const jitter = Math.floor(100 + Math.random() * 300)
    const ms = Math.min(max, Math.floor(base * 2 ** exp) + jitter)
    return ms
  }

  /**
   * 可中断的延迟
   */
  private delayCancellable(ms: number): Promise<void> {
    const signal = this.abortController?.signal
    if (!signal)
      return this.delay(ms)

    return new Promise((resolve, reject) => {
      let timer: any
      const cleanup = () => {
        try { clearTimeout(timer) }
        catch {}
      }
      const onAbort = () => { cleanup(); reject(new Error('executor aborted')) }
      timer = setTimeout(() => { cleanup(); resolve() }, ms)
      try { signal.addEventListener('abort', onAbort as any, { once: true } as any) }
      catch {}
    })
  }

  /**
   * 延迟
   */
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms))
  }

  /**
   * 获取执行器状态
   */
  getStatus(): {
    running: boolean
    processingCount: number
    concurrency: number
    enabled: boolean
  } {
    return {
      running: this.running,
      processingCount: this.processingTasks.size,
      concurrency: this.concurrency,
      enabled: this.enabled,
    }
  }
}

let executor: MonitoringTaskExecutor | null = null

export function getMonitoringTaskExecutor(): MonitoringTaskExecutor {
  if (!executor) {
    // 使用默认配置，未来可以从配置文件读取
    executor = new MonitoringTaskExecutor({
      pollingInterval: 2000, // 2秒轮询一次
      concurrency: 5, // 并发5个任务
      enabled: true, // 默认启用
    })
  }
  return executor
}
